/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.antia.db;

import org.apache.commons.dbcp.BasicDataSource;

import javax.sql.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.PrintWriter;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * A collection of JDBC helper methods.  This class is thread safe.
 */
public final class DbUtils {
    //--------------以下代码 是关于数据库库的配置，需要从外部来盏------------------------------//
    //--------------以下代码 是关于数据库库的配置，需要从外部来盏------------------------------//
    //--------------分别调用setDbType 与 setDataScource 方法即可完成设置---------------------//
    private static DataSource dataSource;
    public static String dbType;

    /**
     * 根据DBASS自身的配置文件初始化数据库配置
     */
    public void init() {
        try {
            InputStream inputStream = this.getClass().getResourceAsStream("/dbass.properties");
            Properties config = new Properties();
            config.load(inputStream);
            //取数据类型
            dbType = config.getProperty("dbtype");
            DataSource dslocal;
            //取数据源
            String ds = config.getProperty("dataSource");
            if (ds != null && !ds.trim().equals("")) {
                Context ctx = new InitialContext();
                String strLookup = "java:comp/env/" + ds;
                dslocal = (DataSource) ctx.lookup(strLookup);
            }else{
                BasicDataSource dataSource = new BasicDataSource();
                // 连接串
                dataSource.setUrl(config.getProperty("url"));
                // 用户
                dataSource.setUsername(config.getProperty("user"));
                // 密码
                dataSource.setPassword(config.getProperty("password"));
                //驱动
                dataSource.setDriverClassName(config.getProperty("driver"));
                dataSource.setMaxWait(Long.parseLong(config.getProperty("maxWait")));
                dataSource.setMaxActive(Integer.parseInt(config.getProperty("maxActive")));
                dataSource.setMaxIdle(Integer.parseInt(config.getProperty("maxIdle")));
                dslocal = dataSource;
            }
            init(dbType, dslocal);
        } catch (Exception e) {
            throw new DbException("初始配置DBASS失败，请检查DBASS的配置文件与配置项。");
        }
    }

    /**
     * 初始化DbAss的方法
     * @param dbType DB type support db : mysql,oracle,sqlserver,postgresql，其中mssql支持为2005以后的版本。
     * @param dataSource DataSource
     */
    public static void init(String dbType,DataSource dataSource) {
        if (dbType == null || dbType.trim().equals("") || dataSource == null) {
            throw new DbException("DbUtils config error:get null dbtype or null datasource");
        }
        setDbType(dbType);
        setDataSource(dataSource);
        //设置SQLGene
        String className;
        if (dbType.equals("mysql")) {
            className = "cn.antia.db.MySQLSQLGene";
        } else if (dbType.equals("oracle")) {
            className = "cn.antia.db.OracleSQLGene";
        } else if (dbType.equals("mssql")) {
            className = "cn.antia.db.SQLServerSQLGene";
        }else if (dbType.equals("postgre")) {
            className = "cn.antia.db.PostgreSQLGene";
        } else {
            throw new DbException("DbUtils unsuppoer db : " + dbType);
        }
        try {
            CommonDAO.sqlGene = (SQLGene) Class.forName(className).newInstance();
        } catch (Exception e) {
            throw new DbException("DbUtils SQLGene config error : " + dbType);
        }
    }

    public static DataSource getDataSource() {
        return dataSource;
    }
    public static void setDataSource(DataSource dataSource) {
        DbUtils.dataSource = dataSource;
    }
    public static String getDbType() {
        return dbType;
    }
    public static void setDbType(String dbType) {
        DbUtils.dbType = dbType;
    }

    public static Connection getConnection() {
        if (TransactionUtils.isInTransaction()) {
            return TransactionUtils.currentConnection();
        }
        if (dataSource == null) {
            new DbUtils().init();
        }
        if (dataSource == null) {
            throw new DbException("DBUtils DataSource not defined");
        }
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new DbException("DBUtils get DB connection failed",e);
        }
    }
    //---------------------以上代码关乎数据库配置---------------------//
    //---------------------以上代码关乎数据库配置---------------------//

    /**
     * Close a <code>Connection</code>, avoid closing if null.
     *
     * @param conn Connection to close.
     * @throws SQLException if a database access error occurs
     */
    public static void close(Connection conn) throws SQLException {
        if (conn != null) {
            if (!TransactionUtils.isConnInTransaction(conn)) {
                conn.close();
            }
        }
    }

    /**
     * Close a <code>ResultSet</code>, avoid closing if null.
     *
     * @param rs ResultSet to close.
     * @throws SQLException if a database access error occurs
     */
    public static void close(ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
    }

    /**
     * Close a <code>Statement</code>, avoid closing if null.
     *
     * @param stmt Statement to close.
     * @throws SQLException if a database access error occurs
     */
    public static void close(Statement stmt) throws SQLException {
        if (stmt != null) {
            stmt.close();
        }
    }

    /**
     * Close a <code>Connection</code>, avoid closing if null and hide
     * any SQLExceptions that occur.
     *
     * @param conn Connection to close.
     */
    public static void closeQuietly(Connection conn) {
        try {
            close(conn);
        } catch (SQLException e) {
            // quiet
        }
    }

    /**
     * Close a <code>Connection</code>, <code>Statement</code> and 
     * <code>ResultSet</code>.  Avoid closing if null and hide any 
     * SQLExceptions that occur.
     *
     * @param conn Connection to close.
     * @param stmt Statement to close.
     * @param rs ResultSet to close.
     */
    public static void closeQuietly(Connection conn, Statement stmt,
            ResultSet rs) {

        try {
            closeQuietly(rs);
        } finally {
            try {
                closeQuietly(stmt);
            } finally {
                closeQuietly(conn);
            }
        }

    }

    /**
     * Close a <code>ResultSet</code>, avoid closing if null and hide any
     * SQLExceptions that occur.
     *
     * @param rs ResultSet to close.
     */
    public static void closeQuietly(ResultSet rs) {
        try {
            close(rs);
        } catch (SQLException e) {
            // quiet
        }
    }

    /**
     * Close a <code>Statement</code>, avoid closing if null and hide
     * any SQLExceptions that occur.
     *
     * @param stmt Statement to close.
     */
    public static void closeQuietly(Statement stmt) {
        try {
            close(stmt);
        } catch (SQLException e) {
            // quiet
        }
    }

    /**
     * Commits a <code>Connection</code> then closes it, avoid closing if null.
     *
     * @param conn Connection to close.
     * @throws SQLException if a database access error occurs
     */
    public static void commitAndClose(Connection conn) throws SQLException {
        if (conn != null) {
            try {
                conn.commit();
            } finally {
                conn.close();
            }
        }
    }

    /**
     * Commits a <code>Connection</code> then closes it, avoid closing if null 
     * and hide any SQLExceptions that occur.
     *
     * @param conn Connection to close.
     */
    public static void commitAndCloseQuietly(Connection conn) {
        try {
            commitAndClose(conn);
        } catch (SQLException e) {
            // quiet
        }
    }

    /**
     * Loads and registers a database driver class.
     * If this succeeds, it returns true, else it returns false.
     *
     * @param driverClassName of driver to load
     * @return boolean <code>true</code> if the driver was found, otherwise <code>false</code>
     */
    public static boolean loadDriver(String driverClassName) {
        try {
            Class.forName(driverClassName).newInstance();
            return true;

        } catch (ClassNotFoundException e) {
            return false;

        } catch (IllegalAccessException e) {
            // Constructor is private, OK for DriverManager contract
            return true;

        } catch (InstantiationException e) {
            return false;

        } catch (Throwable e) {
            return false;
        }
    }

    /**
     * Print the stack trace for a SQLException to STDERR.
     *
     * @param e SQLException to print stack trace of
     */
    public static void printStackTrace(SQLException e) {
        printStackTrace(e, new PrintWriter(System.err));
    }

    /**
     * Print the stack trace for a SQLException to a 
     * specified PrintWriter. 
     *
     * @param e SQLException to print stack trace of
     * @param pw PrintWriter to print to
     */
    public static void printStackTrace(SQLException e, PrintWriter pw) {

        SQLException next = e;
        while (next != null) {
            next.printStackTrace(pw);
            next = next.getNextException();
            if (next != null) {
                pw.println("Next SQLException:");
            }
        }
    }

    /**
     * Print warnings on a Connection to STDERR.
     *
     * @param conn Connection to print warnings from
     */
    public static void printWarnings(Connection conn) {
        printWarnings(conn, new PrintWriter(System.err));
    }

    /**
     * Print warnings on a Connection to a specified PrintWriter. 
     *
     * @param conn Connection to print warnings from
     * @param pw PrintWriter to print to
     */
    public static void printWarnings(Connection conn, PrintWriter pw) {
        if (conn != null) {
            try {
                printStackTrace(conn.getWarnings(), pw);
            } catch (SQLException e) {
                printStackTrace(e, pw);
            }
        }
    }

    /**
     * Rollback any changes made on the given connection.
     * @param conn Connection to rollback.  A null value is legal.
     * @throws SQLException if a database access error occurs
     */
    public static void rollback(Connection conn) throws SQLException {
        if (conn != null) {
            conn.rollback();
        }
    }
    
    /**
     * Performs a rollback on the <code>Connection</code> then closes it, 
     * avoid closing if null.
     *
     * @param conn Connection to rollback.  A null value is legal.
     * @throws SQLException if a database access error occurs
     * @since DbUtils 1.1
     */
    public static void rollbackAndClose(Connection conn) throws SQLException {
        if (conn != null) {
            try {
                conn.rollback();
            } finally {
                conn.close();
            }
        }
    }

    /**
     * Performs a rollback on the <code>Connection</code> then closes it, 
     * avoid closing if null and hide any SQLExceptions that occur.
     *
     * @param conn Connection to rollback.  A null value is legal.
     * @since DbUtils 1.1
     */
    public static void rollbackAndCloseQuietly(Connection conn) {
        try {
            rollbackAndClose(conn);
        } catch (SQLException e) {
            // quiet
        }
    }
}
